package main

import "fmt"
import "reflect"

type F func(string, int) bool

func (f F) m(s string) bool {
	return f(s, 32)
}
func (f F) M() {}

type I interface {
	m(s string) bool
	M()
}

//我们可以通过反射列出一个类型的所有方法和一个结构体类型的所有（导出和非导出）字段的类型。
//我们也可以通过反射列出一个函数类型的各个输入参数和返回结果类型。
//对于非接口类型，reflect.Type.NumMethod方法只返回一个类型的所有导出的方法（包括通过内嵌得来的隐式方法）的个数，
//并且 方法reflect.Type.MethodByName不能用来获取一个类型的非导出方法；
//而对于接口类型，则并无这些限制（Go 1.16之前的文档对这两个方法的描述不准确，并没有体现出这个差异）。
//此情形同样存在于下一节将要介绍的reflect.Value类型上的相应方法。
//虽然reflect.Type.NumField方法返回一个结构体类型的所有字段（包括非导出字段）的数目，
//但是不推荐使用方法reflect.Type.FieldByName来获取非导出字段。
func main() {
	var x struct {
		F F
		i I
	}
	tx := reflect.TypeOf(x)
	fmt.Println("tx.Kind():", tx.Kind())               // struct
	fmt.Println("tx.NumField():", tx.NumField())       // 2
	fmt.Println("tx.Field(0).Name:", tx.Field(0).Name) // F
	fmt.Println("tx.Field(1).Name:", tx.Field(1).Name) // i
	// 包路径（PkgPath）是非导出字段（或者方法）的内在属性。
	fmt.Println("tx.Field(0).PkgPath:", tx.Field(0).PkgPath) //
	fmt.Println("tx.Field(1).PkgPath:", tx.Field(1).PkgPath) // main

	tf, ti := tx.Field(0).Type, tx.Field(1).Type
	fmt.Println("tf.Kind():", tf.Kind())                             // func
	fmt.Println("函数的最后一个参数是否可变参数 tf.IsVariadic():", tf.IsVariadic()) // false
	fmt.Println("tf.NumIn(), tf.NumOut():", tf.NumIn(), tf.NumOut()) // 2 1
	t0, t1, t2 := tf.In(0), tf.In(1), tf.Out(0)
	// 下一行打印出：string int bool
	fmt.Println("t0.Kind(), t1.Kind(), t2.Kind():", t0.Kind(), t1.Kind(), t2.Kind())

	fmt.Println("tf.NumMethod(), ti.NumMethod():", tf.NumMethod(), ti.NumMethod()) // 1 2
	fmt.Println("tf.Method(0).Name:", tf.Method(0).Name)                           // M
	fmt.Println("ti.Method(0).Name:", ti.Method(0).Name)                           // M
	fmt.Println("ti.Method(1).Name:", ti.Method(1).Name)                           // m
	_, ok1 := tf.MethodByName("m")
	_, ok2 := ti.MethodByName("m")
	fmt.Println("ok1, ok2:", ok1, ok2) // false true
}
